package com.webchat.chat.core.shiro;

import com.google.common.collect.Sets;
import com.webchat.chat.core.enums.UserAccountLockedStatus;
import com.webchat.chat.core.enums.UserOnlineStatus;
import com.webchat.chat.core.redis.RedisService;
import com.webchat.chat.core.tools.ChatConstant;
import com.webchat.chat.core.tools.RedisConstant;
import com.webchat.chat.core.tools.SaltUtil;
import com.webchat.chat.model.Tenant;
import com.webchat.chat.model.User;
import com.webchat.chat.service.PermissionService;
import com.webchat.chat.service.RoleService;
import com.webchat.chat.service.TenantService;
import com.webchat.chat.service.UserService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Resource;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;

/**
 * Created by Owner on 2018/6/27.
 */

public class UserRealm extends AuthorizingRealm {

    private static final Logger logger = LoggerFactory.getLogger(UserRealm.class);

    @Resource
    private UserService userService;
    @Resource
    private TenantService tenantService;
    @Resource
    private PermissionService permissionService;
    @Resource
    private RoleService roleService;
    @Resource
    private RedisService redisService;

    /**
     * 授权方法
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        SimpleAuthorizationInfo auth = new SimpleAuthorizationInfo();
        Object obj = principals.getPrimaryPrincipal();
        if (obj instanceof ShiroUser) {
            ShiroUser shiroUser = (ShiroUser) obj;
            String userId = shiroUser.getUserId();

            // 1.用户角色授权
            Set<String> roleNames = Sets.newHashSet();
            roleNames.addAll(roleService.findRoleNamesByUserId(userId));
            auth.setRoles(roleNames);
            // 2.权限
            Set<String> permissions = Sets.newHashSet();
            permissions.addAll(permissionService.findPermissionCodeByUserId(userId));
            auth.addStringPermissions(permissions);
        }
        return auth;
    }

   /**
    * 用户登录验证，先验证平台是否启用，然后验证用户是否启用
    * @date 2018/8/2 11:05
    * @author lion
    *
    */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        UserToken userToken = (UserToken) token;
        String userName = userToken.getUsername();

        //1.查找需要登录的用户
        User user = userService.findBy("userName", userName);
        if (user == null) {
            logger.error("[{}]用户登录失败,不存在此用户", userName);
            throw new UnknownAccountException("账号不存在!");
        }
        //2.账号状态校验
        UserAccountLockedStatus accountLocked = user.getAccountLocked();
        Tenant tenant;
        //验证平台是否启用
        if(!"All".equals(user.getTenantCode())){
           tenant = tenantService.findBy("tenantCode", user.getTenantCode());
            if(!tenant.isEnable()){
                logger.error("[{}]平台未启用异常:{}", userName, accountLocked.name());
                throw new UnknownAccountException("平台未启用异常 : " + accountLocked.name());
            }
        }
        //验证用户是否启用
        if (!Objects.equals(accountLocked, UserAccountLockedStatus.正常)) {
            logger.error("[{}]用户登录失败,用户账号异常:{}", userName, accountLocked.name());
            throw new UnknownAccountException("账号异常:" + accountLocked.name());
        }

        //3.校验密码
        String paswordMd5 = SaltUtil.encodeMd5Hash(userToken.getLoginPassword(), user.getPwdSalt());
        if (!Objects.equals(paswordMd5, user.getPwdLogin())) {
            logger.error("[{}]用户登录失败,密码错误", userName);
            throw new IncorrectCredentialsException("密码错误");
        }
        //生成通讯密码存放Redis
        String pwdConnect = UUID.randomUUID().toString();
        redisService.addMap(RedisConstant.PWD_CONNECT, user.getId(), pwdConnect);
        //登录成功
        ShiroUser shiroUser = new ShiroUser(user.getId(), user.getTenantCode(), userName, user.getAccountType(), pwdConnect, userToken.getLoginIp(), user.getGroupId(), user.getGroupName());
        ByteSource pwdSalt = ByteSource.Util.bytes(user.getPwdSalt());
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(shiroUser, user.getPwdLogin(), pwdSalt, getName());
        //将shiroUser放入shiro session中，方便shiro session失效的时候处理一些跟用户相关的善后工作
        SecurityUtils.getSubject().getSession().setAttribute(ChatConstant.SHIRO_USER_IN_SESSION, shiroUser);


        //修改用户为在线状态, socket连接已经处理
        //user.setOnlineStatus(UserOnlineStatus.在线);
        //userService.update(user);
        return authenticationInfo;
    }
}
